//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty.  In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//

#include "MeshLoaderObj.h"
#include <stdio.h>
#include <stdlib.h>
#include <cstring>

#define _USE_MATH_DEFINES

#include <math.h>

rcMeshLoaderObj::rcMeshLoaderObj() :
  m_scale( 1.0f ),
  m_verts( 0 ),
  m_tris( 0 ),
  m_normals( 0 ),
  m_vertCount( 0 ),
  m_triCount( 0 )
{
}

rcMeshLoaderObj::~rcMeshLoaderObj()
{
  delete[] m_verts;
  delete[] m_normals;
  delete[] m_tris;
}

void rcMeshLoaderObj::addVertex( float x, float y, float z, int& cap )
{
  if( m_vertCount + 1 > cap )
  {
    cap = !cap ? 8 : cap * 2;
    float* nv = new float[cap * 3];
    if( m_vertCount )
      memcpy( nv, m_verts, m_vertCount * 3 * sizeof( float ) );
    delete[] m_verts;
    m_verts = nv;
  }
  float* dst = &m_verts[ m_vertCount * 3 ];
  *dst++ = x * m_scale;
  *dst++ = y * m_scale;
  *dst++ = z * m_scale;
  m_vertCount++;
}

void rcMeshLoaderObj::addTriangle( int a, int b, int c, int& cap )
{
  if( m_triCount + 1 > cap )
  {
    cap = !cap ? 8 : cap * 2;
    int* nv = new int[cap * 3];
    if( m_triCount )
      memcpy( nv, m_tris, m_triCount * 3 * sizeof( int ) );
    delete[] m_tris;
    m_tris = nv;
  }
  int* dst = &m_tris[ m_triCount * 3 ];
  *dst++ = a;
  *dst++ = b;
  *dst++ = c;
  m_triCount++;
}

static char* parseRow( char* buf, char* bufEnd, char* row, int len )
{
  bool start = true;
  bool done = false;
  int n = 0;
  while( !done && buf < bufEnd )
  {
    char c = *buf;
    buf++;
    // multirow
    switch( c )
    {
      case '\\':
        break;
      case '\n':
        if( start )
          break;
        done = true;
        break;
      case '\r':
        break;
      case '\t':
      case ' ':
        if( start )
          break;
        // else falls through
      default:
        start = false;
        row[ n++ ] = c;
        if( n >= len - 1 )
          done = true;
        break;
    }
  }
  row[ n ] = '\0';
  return buf;
}

static int parseFace( char* row, int* data, int n, int vcnt )
{
  int j = 0;
  while( *row != '\0' )
  {
    // Skip initial white space
    while( *row != '\0' && ( *row == ' ' || *row == '\t' ) )
      row++;
    char* s = row;
    // Find vertex delimiter and terminated the string there for conversion.
    while( *row != '\0' && *row != ' ' && *row != '\t' )
    {
      if( *row == '/' )
        *row = '\0';
      row++;
    }
    if( *s == '\0' )
      continue;
    int vi = atoi( s );
    data[ j++ ] = vi < 0 ? vi + vcnt : vi - 1;
    if( j >= n )
      return j;
  }
  return j;
}

bool rcMeshLoaderObj::load( const std::string& filename )
{
  char* buf = 0;
  FILE* fp = fopen( filename.c_str(), "rb" );
  if( !fp )
    return false;
  if( fseek( fp, 0, SEEK_END ) != 0 )
  {
    fclose( fp );
    return false;
  }
  long bufSize = ftell( fp );
  if( bufSize < 0 )
  {
    fclose( fp );
    return false;
  }
  if( fseek( fp, 0, SEEK_SET ) != 0 )
  {
    fclose( fp );
    return false;
  }
  buf = new char[bufSize];
  if( !buf )
  {
    fclose( fp );
    return false;
  }
  size_t readLen = fread( buf, bufSize, 1, fp );
  fclose( fp );

  if( readLen != 1 )
  {
    delete[] buf;
    return false;
  }

  char* src = buf;
  char* srcEnd = buf + bufSize;
  char row[512];
  int face[32];
  float x, y, z;
  int nv;
  int vcap = 0;
  int tcap = 0;

  while( src < srcEnd )
  {
    // Parse one row
    row[ 0 ] = '\0';
    src = parseRow( src, srcEnd, row, sizeof( row ) / sizeof( char ) );
    // Skip comments
    if( row[ 0 ] == '#' )
      continue;
    if( row[ 0 ] == 'v' && row[ 1 ] != 'n' && row[ 1 ] != 't' )
    {
      // Vertex pos
      sscanf( row + 1, "%f %f %f", &x, &y, &z );
      addVertex( x, y, z, vcap );
    }
    if( row[ 0 ] == 'f' )
    {
      // Faces
      nv = parseFace( row + 1, face, 32, m_vertCount );
      for( int i = 2; i < nv; ++i )
      {
        const int a = face[ 0 ];
        const int b = face[ i - 1 ];
        const int c = face[ i ];
        if( a < 0 || a >= m_vertCount || b < 0 || b >= m_vertCount || c < 0 || c >= m_vertCount )
          continue;
        addTriangle( a, b, c, tcap );
      }
    }
  }

  delete[] buf;

  // Calculate normals.
  m_normals = new float[m_triCount * 3];
  for( int i = 0; i < m_triCount * 3; i += 3 )
  {
    const float* v0 = &m_verts[ m_tris[ i ] * 3 ];
    const float* v1 = &m_verts[ m_tris[ i + 1 ] * 3 ];
    const float* v2 = &m_verts[ m_tris[ i + 2 ] * 3 ];
    float e0[3], e1[3];
    for( int j = 0; j < 3; ++j )
    {
      e0[ j ] = v1[ j ] - v0[ j ];
      e1[ j ] = v2[ j ] - v0[ j ];
    }
    float* n = &m_normals[ i ];
    n[ 0 ] = e0[ 1 ] * e1[ 2 ] - e0[ 2 ] * e1[ 1 ];
    n[ 1 ] = e0[ 2 ] * e1[ 0 ] - e0[ 0 ] * e1[ 2 ];
    n[ 2 ] = e0[ 0 ] * e1[ 1 ] - e0[ 1 ] * e1[ 0 ];
    float d = sqrtf( n[ 0 ] * n[ 0 ] + n[ 1 ] * n[ 1 ] + n[ 2 ] * n[ 2 ] );
    if( d > 0 )
    {
      d = 1.0f / d;
      n[ 0 ] *= d;
      n[ 1 ] *= d;
      n[ 2 ] *= d;
    }
  }

  m_filename = filename;
  return true;
}
